home *** CD-ROM | disk | FTP | other *** search
/ QRZ! Ham Radio 4 / QRZ Ham Radio Callsign Database - Volume 4.iso / files / tcpip / amiga / asrc29p.lha / pop2serv.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-12-29  |  13.3 KB  |  680 lines

  1. /* pop2 Server state machine - see RFC 937
  2.  *
  3.  *  also see other credits in pop2cli.c
  4.  *  10/89 Mike Stockett wa7dyx
  5.  *  Modified 5/27/90 by Allen Gwinn, N5CKP, for later NOS releases.
  6.  *  Added to NOS by PA0GRI 2/6/90 (and linted into "standard" C)
  7.  */
  8.  
  9. #include <stdio.h>
  10. #include <time.h>
  11. #include "amiga/stat.h"
  12. #ifdef UNIX
  13. #include <sys/types.h>
  14. #endif
  15. #if    defined(__STDC__) || defined(__TURBOC__)
  16. #include <stdarg.h>
  17. #endif
  18. #include <ctype.h>
  19. #include <setjmp.h>
  20. #include "global.h"
  21. #include "mbuf.h"
  22. #include "cmdparse.h"
  23. #include "socket.h"
  24. #include "proc.h"
  25. #include "files.h"
  26. #include "pop2.h"
  27. #include "commands.h"
  28.  
  29. extern char Nospace[];
  30.  
  31. static struct pop2_scb *create_scb __ARGS((void));
  32. static void delete_scb __ARGS((struct pop2_scb *scb));
  33. static void pop2serv __ARGS((int s,void *unused,void *p));
  34. static int pop2login __ARGS((char *pass,char *username));
  35. void pop2_sm __ARGS((struct pop2_scb *scb));
  36. void rrip __ARGS((register char *s));
  37.  
  38. static int Spop2 = -1; /* prototype socket for service */
  39.  
  40. /* Start up pop2 receiver service */
  41. int pop2start(argc,argv,p)
  42. int argc;
  43. char *argv[];
  44. void *p;
  45. {
  46.     struct sockaddr_in lsocket;
  47.     int s;
  48.  
  49.     if (Spop2 != -1) {
  50.         return 0;
  51.     }
  52.  
  53.     psignal(Curproc,0);        /* Don't keep the parser waiting */
  54.     chname(Curproc,"pop2 listener");
  55.  
  56.     lsocket.sin_family = AF_INET;
  57.     lsocket.sin_addr.s_addr = INADDR_ANY;
  58.     if(argc < 2)
  59.         lsocket.sin_port = IPPORT_POP2;
  60.     else
  61.         lsocket.sin_port = atoi(argv[1]);
  62.  
  63.     Spop2 = socket(AF_INET,SOCK_STREAM,0);
  64.  
  65.     bind(Spop2,(char *)&lsocket,sizeof(lsocket));
  66.  
  67.     listen(Spop2,1);
  68.  
  69.     for (;;) {
  70.         if((s = accept(Spop2,NULLCHAR,(int *)NULL)) == -1)
  71.             break;    /* Service is shutting down */
  72.  
  73.         /* Spawn a server */
  74.  
  75.         newproc("pop2 server",2048,pop2serv,s,NULL,NULL);
  76.     }
  77.     return 0;
  78. }
  79.  
  80. /* Shutdown pop2 service (existing connections are allowed to finish) */
  81. int pop2stop(argc,argv,p)
  82. int argc;
  83. char *argv[];
  84. void *p;
  85. {
  86.     close_s(Spop2);
  87.     Spop2 = -1;
  88.     return 0;
  89. }
  90.  
  91. static void pop2serv(s,unused,p)
  92. int s;
  93. void *unused;
  94. void *p;
  95. {
  96.     struct pop2_scb *scb;
  97.  
  98.     sockowner(s,Curproc);        /* We own it now */
  99.     mainlog(s,"open pop2");
  100.  
  101.     if((scb = create_scb()) == NULLSCB) {
  102.         tprintf(Nospace);
  103.         mainlog(s,"close pop2 - no space");
  104.         close_s(s);
  105.         return;
  106.     }
  107.  
  108.     scb->socket = s;
  109.     scb->state  = AUTH;
  110.  
  111.     (void) usprintf(s,greeting_msg,Hostname);
  112.  
  113. loop:    if ((scb->count = recvline(s,scb->buf,BUF_LEN)) == -1){
  114.         /* He closed on us */
  115.  
  116.         goto quit;
  117.     }
  118.  
  119.     rip(scb->buf);
  120.     if (strlen(scb->buf) == 0)        /* Ignore blank cmd lines */
  121.         goto loop;
  122.     pop2_sm(scb);
  123.     if (scb->state == DONE)
  124.         goto quit;
  125.  
  126.     goto loop;
  127.  
  128. quit:
  129.     mainlog(scb->socket,"close pop2");
  130.     close_s(scb->socket);
  131.     delete_scb(scb);
  132. }
  133.  
  134.  
  135. /* Create control block, initialize */
  136.  
  137. static struct pop2_scb *create_scb()
  138. {
  139.     register struct pop2_scb *scb;
  140.  
  141.     if((scb = (struct pop2_scb *)callocw(1,sizeof (struct pop2_scb))) == NULLSCB)
  142.         return NULLSCB;
  143.  
  144.     scb->username[0] = '\0';
  145.     scb->msg_status = NULL;
  146.     scb->wf = NULL;
  147.  
  148.     scb->count = scb->folder_file_size = scb->msg_num = 0;
  149.  
  150.     scb->folder_modified = FALSE;
  151.     return scb;
  152. }
  153.  
  154.  
  155. /* Free resources, delete control block */
  156.  
  157. static void delete_scb(scb)
  158. register struct pop2_scb *scb;
  159. {
  160.  
  161.     if (scb == NULLSCB)
  162.         return;
  163.     if (scb->wf != NULL)
  164.         fclose(scb->wf);
  165.     if (scb->msg_status  != NULL)
  166.         free((char *)scb->msg_status);
  167.  
  168.     free((char *)scb);
  169. }
  170.  
  171. /* replace terminating end of line marker(s) (\r and \n) with null */
  172. void rrip(s)
  173. register char *s;
  174. {
  175.     register char *cp;
  176.  
  177.     if((cp = strchr(s,'\r')) != NULLCHAR)
  178.         *cp = '\0';
  179.     if((cp = strchr(s,'\n')) != NULLCHAR)
  180.         *cp = '\0';
  181. }
  182.  
  183. /* --------------------- start of pop2 server code ------------------------ */
  184.  
  185. #define    BITS_PER_WORD        16
  186.  
  187. #define isSOM(x)        ((strncmp(x,"From ",5) == 0))
  188.  
  189. /* Command string specifications */
  190.  
  191. static char    ackd_cmd[] = "ACKD",
  192.         acks_cmd[] = "ACKS",
  193. #ifdef POP2_FOLDERS
  194.         fold_cmd[] = "FOLD ",
  195. #endif
  196.         login_cmd[] = "HELO ",
  197.         nack_cmd[] = "NACK",
  198.         quit_cmd[] = "QUIT",
  199.         read_cmd[] = "READ",
  200.         retr_cmd[] = "RETR";
  201.  
  202. void pop2_sm(scb)
  203. struct pop2_scb *scb;
  204. {
  205.     char password[40];
  206.     void state_error(struct pop2_scb *,char *);
  207.     void open_folder(struct pop2_scb *);
  208.     void do_cleanup(struct pop2_scb *);
  209.     void read_message(struct pop2_scb *);
  210.     void retrieve_message(struct pop2_scb *);
  211.     void deletemsg(struct pop2_scb *,int);
  212.     void get_message(struct pop2_scb *,int);
  213.     void print_message_length(struct pop2_scb *);
  214.     void close_folder(struct pop2_scb *);
  215. #ifdef POP2_FOLDERS
  216.     void select_folder(struct pop2_scb *);
  217. #endif
  218.  
  219.  
  220.     switch(scb->state) {
  221.     case AUTH:
  222.         if (strncmp(scb->buf,login_cmd,strlen(login_cmd)) == 0){
  223.             sscanf(scb->buf,"HELO %s%s",scb->username,password);
  224.  
  225.             if (!pop2login(scb->username,password)) {
  226.                 mainlog(scb->socket,"pop2 access DENIED to %s",
  227.                         scb->username);
  228.                 state_error(scb,"Access DENIED!!");
  229.                 return;
  230.             }
  231.  
  232.             mainlog(scb->socket,"pop2 access granted to %s",
  233.                     scb->username);
  234.             open_folder(scb);
  235.         } else if (strncmp(scb->buf,quit_cmd,strlen(quit_cmd)) == 0){
  236.             do_cleanup(scb);
  237.         } else
  238.             state_error(scb,"(AUTH) Expected HELO or QUIT command");
  239.         break;
  240.  
  241.     case MBOX:
  242.         if (strncmp(scb->buf,read_cmd,strlen(read_cmd)) == 0)
  243.             read_message(scb);
  244.  
  245. #ifdef POP2_FOLDERS
  246.         else if (strncmp(scb->buf,fold_cmd,strlen(fold_cmd)) == 0)
  247.             select_folder(scb);
  248.  
  249. #endif
  250.  
  251.         else if (strncmp(scb->buf,quit_cmd,strlen(quit_cmd)) == 0) {
  252.             do_cleanup(scb);
  253.         } else
  254.             state_error(scb,
  255. #ifdef POP2_FOLDERS
  256.                     "(MBOX) Expected FOLD, READ, or QUIT command");
  257. #else
  258.                     "(MBOX) Expected READ or QUIT command");
  259. #endif
  260.         break;
  261.  
  262.     case ITEM:
  263.         if (strncmp(scb->buf,read_cmd,strlen(read_cmd)) == 0)
  264.             read_message(scb);
  265.  
  266. #ifdef POP2_FOLDERS
  267.  
  268.         else if (strncmp(scb->buf,fold_cmd,strlen(fold_cmd)) == 0)
  269.             select_folder(scb);
  270. #endif
  271.  
  272.         else if (strncmp(scb->buf,retr_cmd,strlen(retr_cmd)) == 0)
  273.             retrieve_message(scb);
  274.         else if (strncmp(scb->buf,quit_cmd,strlen(quit_cmd)) == 0)
  275.             do_cleanup(scb);
  276.         else
  277.             state_error(scb,
  278. #ifdef POP2_FOLDERS
  279.                "(ITEM) Expected FOLD, READ, RETR, or QUIT command");
  280. #else
  281.                "(ITEM) Expected READ, RETR, or QUIT command");
  282. #endif
  283.         break;
  284.  
  285.     case NEXT:
  286.         if (strncmp(scb->buf,ackd_cmd,strlen(ackd_cmd)) == 0){
  287.                 /* ACKD processing */
  288.             deletemsg(scb,scb->msg_num);
  289.             scb->msg_num++;
  290.             get_message(scb,scb->msg_num);
  291.         } else if (strncmp(scb->buf,acks_cmd,strlen(acks_cmd)) == 0){
  292.                 /* ACKS processing */
  293.             scb->msg_num++;
  294.             get_message(scb,scb->msg_num);
  295.         } else if (strncmp(scb->buf,nack_cmd,strlen(nack_cmd)) == 0){
  296.                 /* NACK processing */
  297.             fseek(scb->wf,scb->curpos,0);
  298.         } else {
  299.             state_error(scb,"(NEXT) Expected ACKD, ACKS, or NACK command");
  300.             return;
  301.         }
  302.  
  303.         print_message_length(scb);
  304.         scb->state  = ITEM;
  305.         break;
  306.  
  307.     case DONE:
  308.         do_cleanup(scb);
  309.         break;
  310.  
  311.     default:
  312.         state_error(scb,"(TOP) State Error!!");
  313.         break;
  314.     }
  315. }
  316.  
  317. void do_cleanup(scb)
  318. struct pop2_scb *scb;
  319. {
  320.     close_folder(scb);
  321.     (void) usprintf(scb->socket,signoff_msg);
  322.     scb->state = DONE;
  323. }
  324.  
  325. void state_error(scb,msg)
  326. struct pop2_scb *scb;
  327. char *msg;
  328. {
  329.     (void) usprintf(scb->socket,error_rsp,msg);
  330.     scb->state = DONE;
  331. }
  332.  
  333. #ifdef POP2_FOLDERS
  334.  
  335. select_folder(scb)
  336. struct pop2_scb    *scb;
  337. {
  338.     sscanf(scb->buf,"FOLD %s",scb->username);
  339.  
  340.     if (scb->wf != NULL)
  341.         close_folder(scb);
  342.  
  343.     open_folder(scb);
  344. }
  345.  
  346. #endif
  347.  
  348.  
  349. void close_folder(scb)
  350. struct pop2_scb *scb;
  351. {
  352.     char folder_pathname[64];
  353.     char line[BUF_LEN];
  354.     FILE *fd;
  355.     int deleted = FALSE;
  356.     int msg_no = 0;
  357.     struct stat folder_stat;
  358.     int newmail(struct pop2_scb *);
  359.     int isdeleted(struct pop2_scb *,int);
  360.  
  361.     if (scb->wf == NULL)
  362.         return;
  363.  
  364.     if (!scb->folder_modified) {
  365.         /* no need to re-write the folder if we have not modified it */
  366.  
  367.         fclose(scb->wf);
  368.         scb->wf = NULL;
  369.  
  370.         free((char *)scb->msg_status);
  371.         scb->msg_status = NULL;
  372.         return;
  373.     }
  374.  
  375.     sprintf(folder_pathname,"%s/%s.txt",Mailspool,scb->username);
  376.  
  377.     if (newmail(scb)) {
  378.         /* copy new mail into the work file and save the
  379.            message count for later */
  380.  
  381.         if ((fd = fopen(folder_pathname,"r")) == NULL) {
  382.             state_error(scb,"Unable to add new mail to folder");
  383.             return;
  384.         }
  385.  
  386.         fseek(scb->wf,0,2);
  387.         fseek(fd,scb->folder_file_size,0);
  388.         while (!feof(fd)) {
  389.             fgets(line,BUF_LEN,fd);
  390.             fputs(line,scb->wf);
  391.         }
  392.  
  393.         fclose(fd);
  394.     }
  395.  
  396.     /* now create the updated mail folder */
  397.  
  398.     if ((fd = fopen(folder_pathname,"w")) == NULL){
  399.         state_error(scb,"Unable to update mail folder");
  400.         return;
  401.     }
  402.  
  403.     rewind(scb->wf);
  404.     while (!feof(scb->wf)){
  405.         fgets(line,BUF_LEN,scb->wf);
  406.  
  407.         if (isSOM(line)){
  408.             msg_no++;
  409.             if (msg_no <= scb->folder_len)
  410.                 deleted = isdeleted(scb,msg_no);
  411.             else
  412.                 deleted = FALSE;
  413.         }
  414.  
  415.         if (deleted)
  416.             continue;
  417.  
  418.         fputs(line,fd);
  419.     }
  420.  
  421.     fclose(fd);
  422.  
  423.     /* trash the updated mail folder if it is empty */
  424.  
  425.     if ((stat(folder_pathname,&folder_stat) == 0) && (folder_stat.st_size == 0))
  426.         unlink(folder_pathname);
  427.  
  428.     fclose(scb->wf);
  429.     scb->wf = NULL;
  430.  
  431.     free((char *)scb->msg_status);
  432.     scb->msg_status = NULL;
  433. }
  434.  
  435. void open_folder(scb)
  436. struct pop2_scb    *scb;
  437. {
  438.     char folder_pathname[64];
  439.     char line[BUF_LEN];
  440.     FILE *fd;
  441.     FILE *tmpfile();
  442.     struct stat folder_stat;
  443.  
  444.  
  445.     sprintf(folder_pathname,"%s/%s.txt",Mailspool,scb->username);
  446.     scb->folder_len       = 0;
  447.     scb->folder_file_size = 0;
  448.     if (stat(folder_pathname,&folder_stat)){
  449.          (void) usprintf(scb->socket,no_mail_rsp);
  450.          return;
  451.     }
  452.  
  453.     scb->folder_file_size = folder_stat.st_size;
  454.     if ((fd = fopen(folder_pathname,"r")) == NULL){
  455.         state_error(scb,"Unable to open mail folder");
  456.         return;
  457.     }
  458.  
  459.     if ((scb->wf = tmpfile()) == NULL) {
  460.         state_error(scb,"Unable to create work folder");
  461.         return;
  462.     }
  463.  
  464.     while(!feof(fd)) {
  465.         fgets(line,BUF_LEN,fd);
  466.  
  467.         /* scan for begining of a message */
  468.  
  469.         if (isSOM(line))
  470.             scb->folder_len++;
  471.  
  472.         /* now put  the line in the work file */
  473.  
  474.         fputs(line,scb->wf);
  475.     }
  476.  
  477.     fclose(fd);
  478.  
  479.     scb->msg_status_size = (scb->folder_len) / BITS_PER_WORD;
  480.  
  481.     if ((((scb->folder_len) % BITS_PER_WORD) != 0) ||
  482.         (scb->msg_status_size == 0))
  483.         scb->msg_status_size++;
  484.  
  485.     if ((scb->msg_status = (unsigned int *) callocw(scb->msg_status_size,
  486.                 sizeof(unsigned int))) == NULL) {
  487.         state_error(scb,"Unable to create message status array");
  488.         return;
  489.     }
  490.  
  491.     (void) usprintf(scb->socket,count_rsp,scb->folder_len);
  492.  
  493.     scb->state  = MBOX;
  494. }
  495.  
  496. void read_message(scb)
  497. struct pop2_scb    *scb;
  498. {
  499.     if (scb->buf[sizeof(read_cmd) - 1] == ' ')
  500.         scb->msg_num = atoi(&(scb->buf[sizeof(read_cmd) - 1]));
  501.     else
  502.         scb->msg_num++;
  503.  
  504.     get_message(scb,scb->msg_num);
  505.     print_message_length(scb);
  506.     scb->state  = ITEM;
  507. }
  508.  
  509. void retrieve_message(scb)
  510. struct pop2_scb    *scb;
  511. {
  512.     char line[BUF_LEN];
  513.     long cnt;
  514.  
  515.     if (scb->msg_len == 0) {
  516.         state_error(scb,"Attempt to access a DELETED message!");
  517.         return;
  518.     }
  519.  
  520.     cnt  = scb->msg_len;
  521.     while(!feof(scb->wf) && (cnt > 0)) {
  522.         fgets(line,BUF_LEN,scb->wf);
  523.         rrip(line);
  524.  
  525.         (void) usprintf(scb->socket,msg_line,line);
  526.         cnt -= (strlen(line)+2);    /* Compensate for CRLF */
  527.     }
  528.  
  529.     scb->state = NEXT;
  530. }
  531.  
  532. void get_message(scb,msg_no)
  533. struct pop2_scb    *scb;
  534. int msg_no;
  535. {
  536.     char line[BUF_LEN];
  537.     long ftell();
  538.  
  539.     scb->msg_len = 0;
  540.     if (msg_no > scb->folder_len) {
  541.         scb->curpos  = 0;
  542.         scb->nextpos = 0;
  543.         return;
  544.     } else {
  545.         /* find the message and its length */
  546.  
  547.         rewind(scb->wf);
  548.         while (!feof(scb->wf) && (msg_no > -1)) {
  549.             if (msg_no > 0)
  550.                 scb->curpos = ftell(scb->wf);
  551.             
  552.             fgets(line,BUF_LEN,scb->wf);
  553.             rrip(line);
  554.  
  555.             if (isSOM(line))
  556.                 msg_no--;
  557.  
  558.             if (msg_no != 0)
  559.                 continue;
  560.  
  561.             scb->nextpos  = ftell(scb->wf);
  562.             scb->msg_len += (strlen(line)+2);    /* Add CRLF */
  563.         }
  564.     }
  565.  
  566.     if (scb->msg_len > 0)
  567.         fseek(scb->wf,scb->curpos,0);
  568.  
  569.     /* we need the pointers even if the message was deleted */
  570.  
  571.     if  (isdeleted(scb,scb->msg_num))
  572.         scb->msg_len = 0;
  573. }
  574.  
  575. static int pop2login(username,pass)
  576. char *pass;
  577. char *username;
  578. {
  579.     char buf[80];
  580.     char *cp;
  581.     char *cp1;
  582.     FILE *fp;
  583.  
  584.     if((fp = fopen(Pop2users,"r")) == NULLFILE) {
  585.         /* User file doesn't exist */
  586.         tprintf("pop2 users file %s not found\n",Pop2users);
  587.         return(FALSE);
  588.     }
  589.  
  590.     while(fgets(buf,sizeof(buf),fp),!feof(fp)) {
  591.         if(buf[0] == '#')
  592.             continue;    /* Comment */
  593.  
  594.         if((cp = strchr(buf,':')) == NULLCHAR)
  595.             /* Bogus entry */
  596.             continue;
  597.  
  598.         *cp++ = '\0';        /* Now points to password */
  599.         if(strcmp(username,buf) == 0)
  600.             break;        /* Found user name */
  601.     }
  602.  
  603.     if(feof(fp)) {
  604.         /* User name not found in file */
  605.  
  606.         fclose(fp);
  607.         return(FALSE);
  608.     }
  609.     fclose(fp);
  610.  
  611.     if ((cp1 = strchr(cp,':')) == NULLCHAR)
  612.         return(FALSE);
  613.  
  614.     *cp1 = '\0';
  615.     if(strcmp(cp,pass) != 0) {
  616.         /* Password required, but wrong one given */
  617.  
  618.         return(FALSE);
  619.     }
  620.  
  621.     /* whew! finally made it!! */
  622.  
  623.     return(TRUE);
  624. }
  625.  
  626. int isdeleted(scb,msg_no)
  627. struct pop2_scb *scb;
  628. int msg_no;
  629. {
  630.     unsigned int mask = 1,offset;
  631.  
  632.     msg_no--;
  633.     offset = msg_no / BITS_PER_WORD;
  634.     mask <<= msg_no % BITS_PER_WORD;
  635.     return (((scb->msg_status[offset]) & mask)? TRUE:FALSE);
  636. }
  637.  
  638. void deletemsg(scb,msg_no)
  639. struct pop2_scb *scb;
  640. int msg_no;
  641. {
  642.     unsigned int mask = 1,offset;
  643.  
  644.     msg_no--;
  645.     offset = msg_no / BITS_PER_WORD;
  646.     mask <<= msg_no % BITS_PER_WORD;
  647.     scb->msg_status[offset] |= mask;
  648.     scb->folder_modified = TRUE;
  649. }
  650.  
  651. int newmail(scb)
  652. struct pop2_scb *scb;
  653. {
  654.     char folder_pathname[64];
  655.     struct stat folder_stat;
  656.  
  657.     sprintf(folder_pathname,"%s/%s.txt",Mailspool,scb->username);
  658.  
  659.     if (stat(folder_pathname,&folder_stat)) {
  660.         state_error(scb,"Unable to get old mail folder's status");
  661.         return(FALSE);
  662.     } else
  663.         return ((folder_stat.st_size > scb->folder_file_size)? TRUE:FALSE);
  664. }
  665.  
  666. void print_message_length(scb)
  667. struct pop2_scb *scb;
  668. {
  669.     char *print_control_string;
  670.  
  671.     if (scb->msg_len > 0)
  672.         print_control_string = length_rsp;
  673.     else if (scb->msg_num <= scb->folder_len)
  674.         print_control_string = length_rsp;
  675.     else
  676.         print_control_string = no_more_rsp;
  677.  
  678.     (void)usprintf(scb->socket,print_control_string,scb->msg_len,scb->msg_num);
  679. }
  680.